using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using Nvigorate.Common;
using Nvigorate.Common.Reflection;

namespace Nvigorate.Test.Relational.Binding
{
    public class DataFactory
    {
        private static string[] letters = new[]
                                              {
                                                  "A", "B", "C", "D", "E", "F", "G", "H", "I",
                                                  "J", "K", "L", "M", "N", "O", "P", "Q", "R",
                                                  "S", "T", "U", "V", "W", "X", "Y", "Z",
                                                  "a", "b", "c", "d", "e", "f", "g", "h", "i",
                                                  "j", "k", "l", "m", "n", "o", "p", "q", "r",
                                                  "s", "t", "u", "v", "w", "x", "y", "z",
                                                  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"
                                              };

        private static Dictionary<Type, Func<object>> _typeFactory = new Dictionary<Type, Func<object>>
                                                                             {
                                                                                 {typeof(string), () =>
                                                                                                      {
                                                                                                          string result = "";
                                                                                                          int limit = RandomInt(6, 12);
                                                                                                          for(int i = 0; i < limit; i++)
                                                                                                          {
                                                                                                              result += letters[RandomInt(0, 61)];
                                                                                                          }
                                                                                                          return (object) result;
                                                                                                      }},
                                                                                 {typeof(short), () => (short) (RandomDouble() * short.MaxValue)},
                                                                                 {typeof(int), () => (int) (RandomDouble() * int.MaxValue)},
                                                                                 {typeof(long), () => (long) (RandomDouble() * long.MaxValue)},
                                                                                 {typeof(Single), () => (Single) (RandomDouble() * Single.MaxValue)},
                                                                                 {typeof(Double), () => (RandomDouble() * Double.MaxValue)},
                                                                                 {typeof(decimal), () => (decimal) (RandomDouble() * (double) decimal.MaxValue)},
                                                                                 {typeof(bool), () => (bool) (RandomInt(0, 512) % 2 == 0)},
                                                                                 {typeof(DateTime), () => (DateTime) DateTime.FromFileTime((long) RandomInt(0, int.MaxValue))},
                                                                             };


        private DataTable _table = null;
        private List<Type> _columnTypes = new List<Type>();
        private static Random _random = new Random();

        private static int RandomInt(int min, int max)
        {
            return _random.Next(min, max);
        }

        private static Double RandomDouble()
        {
            return _random.NextDouble();
        }

        public DataTable Table
        {
            get { return _table; }
            set { _table = value; }
        }

        public DataRow GetRow()
        {
            DataRow row = _table.NewRow();
            row.ItemArray = _columnTypes.Select(c => _typeFactory[c]()).ToArray();
            _table.Rows.Add(row);
            return row;
        }

        public DataFactory(Type rowType)
        {
            _table = new DataTable(rowType.Name);

            foreach (var property in Reflector.GetProperties(rowType))
            {
                Type columnType = Reflector.GetMemberType(rowType, property);
                if(_typeFactory.ContainsKey(columnType))
                {
                    _table.Columns.Add(property, columnType);
                    _columnTypes.Add(columnType);
                }
            }
        }

        public DataFactory(string[] columnNames, Type[] columnTypes)
        {
            _table = new DataTable();
            
            for(int i = 0; i < columnNames.Length; i++)
            {
                _table.Columns.Add(columnNames[i], columnTypes[i]);
            }
            _columnTypes.AddRange(columnTypes);
        }

        public DataFactory(DataTable table)
        {
            _table = table;
            foreach (DataColumn column in _table.Columns)
            {
                _columnTypes.Add(column.DataType);
            }   
        }
    }
}